データはbufにコピーされる
• ioctl(sockfd, FIONREAD , &nbytes);
使えるOSは限られる(Linuxでは使える)
35
write()
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
unsigned char buf[4];
ssize_t n;
buf[0] = 0x5a;
buf[1] = 0x5b;
buf[2] = 0x5c;
buf[3] = 0x5b;
if (write(sockfd, buf, 4) == -1) { perror("write error");
exit(1);
}
ソケットセンドバッファに余裕がないときにはブ ロックする(エラーにはならない)。
ブロックしないようにするにはノンブロックキグ ソケットオプションを使う(ノンブロッキングにす るとエラー処理とかでだいぶ行数が増える)。
2013-08-27 DAQ-Middlewareトレーニングコース
socket send/receive buffer の大きさ
application
TCP
IP
datalink
application buffer write()
socket send buffer
user process kernel
socket receive buffer
application buffer
read()
socket send/receive buffer の大きさの調整
• 受信に関しては Linux では自動調節機能がある
• 多重読み出しを行うときにはあらかじめ大きくしてお かないと性能がでないことが多い
2013-08-27 DAQ-Middlewareトレーニングコース 37
echo 0 > /proc/sys/net/ipv4/tcp_timestamps
echo 1 > /proc/sys/net/ipv4/tcp_moderate_rcvbuf echo 4194304 > /proc/sys/net/core/wmem_max
echo 4194304 > /proc/sys/net/core/rmem_max
echo 4194304 > /proc/sys/net/core/wmem_default echo 4194304 > /proc/sys/net/core/rmem_default
echo 4096 131072 4194304 > /proc/sys/net/ipv4/tcp_rmem echo 4096 131072 4194304 > /proc/sys/net/ipv4/tcp_wmem
# 131072 128kB
がデフォルト値。もっと大きくしておかないとだめな場合もある
#
あるいはソケット個別に大きくすることもできる(
setsockopt())ソケットレシーブバッファの大きさ調節による改善例
多重読み出しで複数モジュールから読み出し
各モジュールは同一レートでデータを送ってくるようにセット 読むモジュール数を
1, 2, 3,と増加させていった。
調節前 調節後
ここまでのまとめ
• ソケットファイルディスクリプタを取得するとあ とは通常のファイルの読み書きと同様
• ファイルを読むときとは違って指定したサイズ が必ずしも読めるとは限らない。指定したサ イズ必ず読みたければそのような関数を作る 必要がある。
2013-08-27 DAQ-Middlewareトレーニングコース 39
ネットワークバイトオーダー (1)
• unsigned char buf[10];
アドレスは buf[0], buf[1], buf[2] の順に大きくなる
• unsigned char buf[10];
write(sockfd, buf, 10) ;
とすると buf[0], buf[1], buf[2] … の順に送られる。
• read(sockfd, buf, 10);
きた順に buf[0], buf[1], buf[2] に格納される。
41
ネットワークバイトオーダー (2)
// int
がどういう順番でメモリーに
// 入っているか調べるプログラム
#include <stdio.h>
int main(int argc, char *argv[]) {
int i;
union num_tag {
unsigned char c[sizeof(int)];
unsigned int num;
} u_num;
u_num.num = 0x01020304;
for (i = 0; i < sizeof(int); i++) {
printf("u_num.c[%d]: %p 0x%02x ¥n", i, &u_num.c[i], u_num.c[i]);
}
return 0;
}
出力
(i386)u_num.c[0]: 0xbfbfe850 0x04 u_num.c[1]: 0xbfbfe851 0x03 u_num.c[2]: 0xbfbfe852 0x02 u_num.c[3]: 0xbfbfe853 0x01
2013-08-27 DAQ-Middlewareトレーニングコース
ネットワークバイトオーダー (3)
big endian では 0x 01020304 = 16909060 little endian では 0x 04030201 = 67305985 ネットワークバイトオーダーは big endian
0x 01 02 03 04
の順に送られてきたデータを
read(sockfd, buf, 4)で読んだ場合
0x01 0x02 0x03 0x04 0x01 0x02 0x03 0x04
big endian little endian
buf buf
43
ネットワークバイトオーダー (4)
• ホストオーダー⇔ネットワークバイトオーダー 変換関数
– htonl (host to network long) – htons (host to network short) – ntohl (network to host long) – ntohs (network to host short)
2013-08-27 DAQ-Middlewareトレーニングコース
daytime client (1)
• xinetd 内蔵サーバー daytime (port 13)
• /etc/xinetd.d/daytime-stream にて disable = no
に変更して service xinetd restart
• telnet localhost 13
すると現在日時が表示される
2013-08-27 DAQ-Middlewareトレーニングコース 45
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAXLINE 1024
int main(int argc, char *argv[]) {
unsigned char line[MAXLINE + 1];
struct sockaddr_in servaddr;
char *ip_address = "127.0.0.1";
int port = 13;
int sockfd, n;
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
n = inet_pton(AF_INET, ip_address, &servaddr.sin_addr);
if (n < 0) {
perror("inet_pton");
exit(1);
}
else if (n == 0) {
fprintf(stderr, "invalid address %s", ip_address);
exit(1);
}
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket");
exit(1);
}
if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) { perror("connect");
exit(1);
}
2013-08-27 DAQ-Middlewareトレーニングコース 47
for ( ; ; ) {
n = read(sockfd, line, MAXLINE);
if (n < 0) {
perror("read");
exit(1);
}
else if (n == 0) { printf("EOF¥n");
break;
}
line[n] = '¥0'; /* string termination */
printf("%s¥n", line);
}
if (close(sockfd) < 0) { perror("close");
exit(1);
}
return 0;
}
0123456789012345678901234 5 6 07 AUG 2012 13:02:10 JST¥r¥n¥0
[daq@localhost daytimeclient]$ ./daytimeclient | hexdump -vC
00000000 30 37 20 41 55 47 20 32 30 31 32 20 31 33 3a 30 |07 AUG 2012 13:0|
00000010 32 3a 31 30 20 4a 53 54 0d 0a 0a 45 4f 46 0a |2:10 JST...EOF.|
情報のありか
• Manual Page
• 本
49
Manual Pages
• セクション
– 1 (Utility Program) – 2 (System call)
– 3 (Library) – 4 (Device)
– 5 (File format) – 6 (Game)
– 7 (Misc.)
– 8 (Administration)
Linux だとこの他 – 3P (Posix)
2013-08-27 DAQ-Middlewareトレーニングコース
Manual Pages
• man コマンド
• Linux のマニュアルページは
– http://www.kernel.org/doc/man-pages/
– 最新のマニュアルはここで読める。
– 利用している kernel 、 library 等のバージョンに注
意する必要がある。
51
Manual Pages
• Header
READ(3P) POSIX Programmer's Manual READ(3P) READ(2) Linux Programmer's Manual READ(2)
• SYNOPSIS
• DESCRIPTION
• RETURN VALUE
• SEE ALSO
• EXAMPLE
2013-08-27 DAQ-Middlewareトレーニングコース
Manual Pages( 例題 )
READ(2) Linux Programmer's Manual READ(2) NAME
read - read from a file descriptor SYNOPSIS
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
DESCRIPTION
read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.
:
RETURN VALUE
:
ERRORS
:
CONFORMING TO
SVr4, 4.3BSD, POSIX.1-2001.
NOTES
:
53
Manual Pages( 例題 )
READ(2) Linux Programmer's Manual READ(2) NAME
read - read from a file descriptor SYNOPSIS
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
DESCRIPTION
read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.
:
RETURN VALUE
:
ERRORS
:
CONFORMING TO
SVr4, 4.3BSD, POSIX.1-2001.
NOTES
:
SEE ALSO
2013-08-27 DAQ-Middlewareトレーニングコース
Utility
• gettimeofday()
• nc
• tcpdump 、 wireshark (ex. ethereal)
55
gettimeofday() で現在時刻の取得
struct timeval start, end, diff;
if (gettimeofday(&start, NULL) < 0) { err(1, "gettimeofday");
}
/* ... */
if (getimeofday(&end, NULL) < 0) { err(1, "gettimeofday");
}
/*
時間差をとるには引き算してもよいし、
timersub()関数を使ってもよい
timersub(&end, &start, &diff);printf("%ld.%06ld¥n", result.tv_sec, result.tv_usec);
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
Linux
では
gettimeofday()を
1,000,000回繰り返して1秒以下(
CPUに依存する)
2013-08-27 DAQ-Middlewareトレーニングコース
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
ナノ秒まで必要なとき
clock_gettime(CLOCK_REALTIME, &ts);
コンパイル時に
-lrtが必要
struct timespec {time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
余談: 最近のファイルシステムのタイムスタンプはナノ秒まで記録されている
% touch X
% ls -l --full X
-rw-rw-r-- 1 sendai sendai 0 2012-08-02 15:02:55.362116699 +0900 X
nc (netcat)
• nc - arbitrary TCP and UDP connections and listens
• nc 192.168.0.16 > datafile で接続してデータを とってみる
• nc 192.168.0.16 | tee log.dat | prog_histo
2013-08-27 DAQ-Middlewareトレーニングコース 57
% nc -l 1234
(
これで待機して別の端末から
) Hello, world% nc 127.0.0.1 1234 Hello, world
tcpdump
• ネットワーク上を流れているパケットを見るコマンド – 接続できないんだけどパケットはでているのか?
– データが読めないんだけど向こうからパケットは きているんでしょうか?
• root にならないと使えない
• 起動方法
# tcpdump -n -w dumpfile -i eth0 # tcpdump -n -r dumpfile
• Selector
# tcpdump -n -r host 192.168.0.16
# tcpdump -n -r src 192.168.0.16 and dst 192.168.0.17
59
tcpdump 出力例
TCP の 3way ハンドシェイク付近:
11:27:55.137827 IP 192.168.0.16.59448 > 192.168.0.17.http: S 153443204:
153443204(0) win 5840 <mss 1460,sackOK,timestamp 587094474 0,nop,wscale 7>
11:27:55.139573 IP 192.168.0.17.http > 192.168.0.16.59448: S 4091282933:
4091282933(0) ack 153443205 win 65535 <mss 1460,nop,wscale 1,nop,nop,timestamp 3029380287 587094474,sackOK,eol>
11:27:55.139591 IP 192.168.0.16.59448 > 192.168.0.17.http: . ack 1 win 46
<nop,nop,timestamp 587094479 3029380287>
11:27:55.139751 IP 192.168.0.16.59448 > 192.168.0.17.http: P 1:103(102) ack 1 win 46 <nop,nop,timestamp 587094479 3029380287>
11:27:55.143520 IP 192.168.0.17.http > 192.168.0.16.59448: P 1:252(251) ack103 win 33304 <nop,nop,timestamp 3029380290 587094479>
2013-08-27 DAQ-Middlewareトレーニングコース
tcpdump - 時刻情報
• 絶対時刻ではなくて相対的な時間に変換する プログラムを作っておくと便利なことがある。
0.000000 0.000000 IP 192.168.0.16.59448 > 192.168.0.17.http: S 153443204:1534432 0.001746 0.001746 IP 192.168.0.17.http > 192.168.0.16.59448: S 4091282933:409128 0.001764 0.000018 IP 192.168.0.16.59448 > 192.168.0.17.http: . ack 1 win 46 <nop 0.001924 0.000160 IP 192.168.0.16.59448 > 192.168.0.17.http: P 1:103(102) ack 1 0.005693 0.003769 IP 192.168.0.17.http > 192.168.0.16.59448: P 1:252(251) ack 10 0.005703 0.000010 IP 192.168.0.16.59448 > 192.168.0.17.http: . ack 252 win 54 <n 1.107822 1.102119 IP 192.168.0.16.59448 > 192.168.0.17.http: F 103:103(0) ack 25 1.108482 0.000660 IP 192.168.0.17.http > 192.168.0.16.59448: . ack 104 win 33304 1.109608 0.001126 IP 192.168.0.17.http > 192.168.0.16.59448: F 252:252(0) ack 10 1.109618 0.000010 IP 192.168.0.16.59448 > 192.168.0.17.http: . ack 253 win 54 <n
最初の欄は
SYNを送ってからの経過時間
2番目の欄は直前の行との時間差を示すもの
61
tcpdump + program log
• tcpdump の時刻情報と同じ時刻フォーマットでロ
グを出すようにしておいて tcpdump をとりつつプ ログラムを走らせあとからマージする:
(tcpdump -n -r tcpdump.out; cat log) | sort -n
2013-08-27 DAQ-Middlewareトレーニングコース
NEUNET Protocol
クライアント サーバー(検出器モジュール)
length request
length + data
length request
length + data
63
tcpdump + program log
0.000000 0.000000 connect start
0.000063 0.000063 IP 192.168.0.204.57447 > 192.168.0.20.telnet: S 4076228960:407 0.000128 0.000065 IP 192.168.0.20.telnet > 192.168.0.204.57447: S 3718362368:371 0.000159 0.000031 IP 192.168.0.204.57447 > 192.168.0.20.telnet: . ack 1 win 5840 0.000215 0.000056 write length
0.000227 0.000012 IP 192.168.0.204.57447 > 192.168.0.20.telnet: P 1:9(8) ack 1 w 0.000234 0.000007 read length + data
0.000275 0.000041 IP 192.168.0.20.telnet > 192.168.0.204.57447: . ack 9 win 6551 0.002269 0.001994 IP 192.168.0.20.telnet > 192.168.0.204.57447: . 1:5(4) ack 9 w 0.002284 0.000015 IP 192.168.0.204.57447 > 192.168.0.20.telnet: . ack 5 win 5840 0.002300 0.000016 write length
0.002306 0.000006 IP 192.168.0.204.57447 > 192.168.0.20.telnet: P 9:17(8) ack 5 0.002312 0.000006 read length + data
0.002369 0.000057 IP 192.168.0.20.telnet > 192.168.0.204.57447: . ack 17 win 655 0.002568 0.000199 IP 192.168.0.20.telnet > 192.168.0.204.57447: . 5:1465(1460) a 0.002583 0.000015 IP 192.168.0.204.57447 > 192.168.0.20.telnet: . ack 1465 win 8 0.002717 0.000134 IP 192.168.0.20.telnet > 192.168.0.204.57447: . 1465:2925(1460
2013-08-27 DAQ-Middlewareトレーニングコース
wireshark
• yum install wireshark-gnome (GUI つきのをイ ンストールする)
• Ethernet 、 IP, TCP のヘッダがどこか色つきで
表示してくれるので便利
2013-08-27 DAQ-Middlewareトレーニングコース 65
MACアドレスか
らベンダーを調 べて表示する
(らしい)
wireshark
• 複数の TCP セッションが あっても
Analyze→Follow TCP
Stream で追跡可能
wireshark
2013-08-27 DAQ-Middlewareトレーニングコース 67
wireshark
• データのダンプもできる
wireshark
• フローグラフ
2013-08-27 DAQ-Middlewareトレーニングコース 69